#ifndef POLYGON3D_H
#define POLYGON3D_H

#include <vector>
#include "../utils/Point3.h"
#include "../utils/AABB3D.h"
#include "../settings/types/AngleDegrees.h" //Infill angles.
#include "../sliceDataStorage.h"

namespace cura {


class Polygon3D : public std::vector<Point3> {
public:
	Polygon3D() {}
	Polygon3D(int size) : std::vector<Point3>(size){}
public:
	AABB3D aabb() const { return m_aabb; }

	void traverse(std::function<void (Point3&)> callback);
	// std::function<void(int, const Point&, Point3&)> callback  -----> void(nLayer, Path[i], this[i])
	void fromPolygon2D(const ClipperLib::Path& data, int nLayer, std::function<void(int, const Point&, Point3&)> callback);

	bool isOverlap(const Polygon3D& other) const;
private:
	AABB3D m_aabb;


};

Polygon& Polygon2DFromPolygon3D(Polygon& polygon2d, const Polygon3D& polygon3d, coord_t zHeight, std::function<Point(coord_t, const Point3&)> callback);

class Polygon3Ds : public std::vector<Polygon3D> {
public:
	Polygon3Ds() {}
	Polygon3Ds(int size) : std::vector<Polygon3D>(size){}
public:
	AABB3D aabb() { return m_aabb; }

	Polygon3D& outerPolygon() {
		return at(0);
	}

	void traverse(std::function<void(Point3&)> callback);

	void fromPolygon2Ds(const Polygons& data, int nLayer, std::function<void(int, const Point&, Point3&)> callback);
private:
	AABB3D m_aabb;
};

PolygonsPart& Polygon2DsFromPolygon3Ds(PolygonsPart& polygon2ds, const Polygon3Ds& polygon3ds, coord_t zHeight, std::function<Point(coord_t, const Point3&)> callback);

class SupportInfillPart3D {
public:
	SupportInfillPart3D() {}
public:
	AABB3D aabb() { return m_aabb; }

	coord_t support_line_width;  //!< The support line width
	int inset_count_to_generate;  //!< The number of insets need to be generated from the outline. This is not the actual insets that will be generated.

	Polygon3Ds outline;  //!< The outline of the support infill area
	//std::vector<Polygon3Ds> insets;  //!< The insets are also known as perimeters or the walls.
	//AABB3D outline_boundary_box;  //!< The boundary box for the infill area
	std::vector<std::vector<Polygon3Ds>> infill_area_per_combine_per_density;  //!< a list of separated sub-areas which requires different infill densities and combined thicknesses



	void traverse(std::function<void(Point3&)> callback);

	void fromSupportInfillPart2D(const SupportInfillPart& data, int nLayer, std::function<void(int, const Point&, Point3&)> callback);
private:
	//Polygon3Ds infill_area;  //!< The support infill area for generating patterns
	AABB3D m_aabb;
};

//PolygonsPart SupportInfillPart2DFromSupportInfillPart3D(const SupportInfillPart3D& supportInfillPart3d, coord_t zHeight, std::function<Point(coord_t, const Point3&)> callback);

class SupportLayer3D
{
public:
	SupportLayer3D() {}
public:
	AABB3D aabb() { return m_aabb; }

	std::vector<SupportInfillPart3D> support_infill_parts;  //!< a list of support infill parts
	Polygon3Ds support_bottom        ; //!< Piece of support below the support and above the model. This must not overlap with any of the support_infill_parts or support_roof.
	Polygon3Ds support_roof          ; //!< Piece of support above the support and below the model. This must not overlap with any of the support_infill_parts or support_bottom.
	Polygon3Ds support_mesh_drop_down; //!< Areas from support meshes which should be supported by more support
	Polygon3Ds support_mesh          ;           //!< Areas from support meshes which should NOT be supported by more support
	Polygon3Ds anti_overhang         ;          //!< Areas where no overhang should be detected.

	void traverse(std::function<void(Point3&)> callback);

	void fromSupportLayer2D(const SupportLayer& data, int nLayer, std::function<void(int, const Point&, Point3&)> callback);
private:
	AABB3D m_aabb;
};

SupportLayer& SupportLayer2DFromSupportLayer3DList(SupportLayer& supportLayer2d, const std::vector<SupportLayer3D*>& supportLayer3DList, coord_t zHeight, std::function<Point(coord_t, const Point3&)> callback, std::function<Polygons(coord_t)> generatePolygon, std::map<coord_t, std::vector<SliceLayerPart*>>& layerPolygons);

class SupportStorage3D
{
public:
	SupportStorage3D();
public:
	AABB3D aabb() { return m_aabb; }

	bool generated; //!< whether generateSupportGrid(.) has completed (successfully)

	int layer_nr_max_filled_layer; //!< the layer number of the uppermost layer with content

	std::vector<AngleDegrees> support_infill_angles        ; //!< a list of angle values which is cycled through to determine the infill angle of each layer
	std::vector<AngleDegrees> support_infill_angles_layer_0; //!< a list of angle values which is cycled through to determine the infill angle of each layer
	std::vector<AngleDegrees> support_roof_angles          ; //!< a list of angle values which is cycled through to determine the infill angle of each layer
	std::vector<AngleDegrees> support_bottom_angles        ; //!< a list of angle values which is cycled through to determine the infill angle of each layer

	std::vector<SupportLayer3D> supportLayers;

	void traverse(std::function<void(Point3&)> callback);

	void fromSupportStorage2D(const SupportStorage& data, std::function<void(int, const Point&, Point3&)> callback);
private:
	AABB3D m_aabb;
};

SupportStorage& SupportStorage2DFromSupportStorage3D(SupportStorage& supportStorage2d, const SupportStorage3D& supportStorage3d, const std::vector<coord_t>& zHeights, const std::vector<std::vector<SupportLayer3D*>>& indexMap, std::function<Point(coord_t, const Point3&)> callback, std::function<Polygons(coord_t)> generatePolygon, std::map<coord_t, std::vector<SliceLayerPart*>>& layerPolygons);

}
#endif